package fr.upmc.aladyn.onloadreflect;

import fr.upmc.aladyn.annotation.Transactionable;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;
import javassist.Translator;
import javassist.expr.ExprEditor;
import javassist.expr.Handler;

/**
 * Class implementing the Translator interface. Used to inject code when a 
 * class is loaded in the environment. Instead of directly loading the class 
 * into the standard Java loader, we load it with a Javassist loader and 
 * inject our code and then restore it so that the Java loader can have it.
 * 
 * @author Thierry Wong
 * @author Seyyid ahmed Ouir 
 */

public class MyTranslator implements Translator {
	
	private String singleton = 
			"fr.upmc.aladyn.shared.ObjectSingleton.getInstance()";
	
	@Override
	public void start(ClassPool pool) throws NotFoundException,
	CannotCompileException {}
	
	@Override
	public void onLoad(ClassPool pool, String myApp) 
			throws NotFoundException, CannotCompileException 
	{
		CtClass cc = pool.get(myApp);
		
		// If current Object has the Transactionable annotation,
		if (cc.hasAnnotation(Transactionable.class))
		{
			// Save fields and add treatment to Transactionable methods
			for (CtMethod m : cc.getDeclaredMethods())
			{
				injectSave(m);
				injectTreatment(m, pool);				
			}
		}
		else
		{
			// Add treatment to Transactionable methods only
			for (CtMethod m : cc.getDeclaredMethods())
			{
				injectTreatment(m, pool);				
			}
		}
	}
	
	/**
	 * Internal method that injects into a method the treatment to save the 
	 * object into the transaction environment if it is a setter.
	 * 
	 * @param m Type <code> Method </code> represents method being manipulated
	 * @throws CannotCompileException Error in compilation.
	 */
	private void injectSave(CtMethod m) throws CannotCompileException
	{
		// If method is a setter, add a save line.
		// Consists in invoking sauvegarder from ObjectSingleton class.
		if (m.getName().startsWith("set")) 
		{					
			m.insertBefore(singleton + ".sauvegarder(this);");
		}
	}
	
	/**
	 * Internal method that injects into a method the treatment to enable the 
	 * restoration of the object to it's state at the beginning of the 
	 * transaction in case of an exception being thrown. 
	 * 
	 * @param m Type <code> Method </code> represents method being manipulated
	 * @param pool Type <code>Classpool</code> 
	 * @throws CannotCompileException Compilation issue
	 * @throws NotFoundException Exception from addCatch not accessible
	 */
	private void injectTreatment(CtMethod m, ClassPool pool) throws 
		CannotCompileException, NotFoundException
	{
		if (m.hasAnnotation(Transactionable.class))
		{
			m.insertBefore(singleton + ".debutMethodeTrans();");
			m.insertAfter(singleton + ".finMethodeTrans();");
			
			ExprEditor editor = new ExprEditor()
			{
				public void edit(Handler internalCatch) throws
					CannotCompileException
				{
					internalCatch.insertBefore(
						singleton + ".restaurer();"
					);
				}					
			};
			m.instrument(editor);
			m.addCatch("{" + singleton + ".restaurerCatchFinal(); throw $e;}", 
						pool.get("java.lang.Exception"));
		}
	}
}
